/* 
    global.name = 'global';
    var getName = function() {
        console.log(this.name);
    }
    console.log(getName());
 */

/* 
global.name = 'global';
var myObj = {
    name: 'sven',
    getName: function() {
        console.log(this.name)
    }
}

myObj.getName(); //sven
var getName = myObj.getName;
getName(); // global
 */
/* 
document.getElementById('div1').onclick = function() {
    // 'use strict';
    console.log(this.id);
    var callback = function() {
        console.log(this)
    }
    callback() // this 指向 window  因为没有函数的调用者
}
*/


/* // 当函数当作构造函数（使用new运算符来）调用时，如果函数没有返回值，则返回新创建的对象，如果函数返回一个Object类型的对象，则直接返回该对象，如果返回非Object类型的数据，返回创建的对象
var myClass = function(name) {
    this.name = name;
    return {key: 'time'};
}

var c = new myClass('222');
console.log(c); */

/* 
// Function.prototype.call/Function.prototype.apply的调用
var obj1 = {
    name: 'sven',
    getName: function() {
        console.log(this.name);
    }
};

var obj2 = {
    name: 'anne'
};

console.log(obj1.getName()); // sven
console.log(obj1.getName.call(obj2)); // anne 
*/

/* var getId_1 = function(id) {
    return document.getElementById(id)
};

var getId_2 = (function(scope) {
    return function(id) {
        return scope.getElementById(id);
    }
})(document);

// 当调用getId_3时 this发生改变
var getId_3 = document.getElementById;

console.log(getId_1('div1'));
console.log(getId_2('div1'));
console.log(getId_3('div1')); 
*/

/* 
// call 与 apply方法是一摸一样的，call是在apply基础上包装的语法糖，apply在传递参数时是以数组与类数组的方式传递给函数的，在函数内部接受参数也是以数组与类数组的方式进行接受的，所以apply的执行效率高于call

// 使用apply 或者 call 改写getId
document.getElementById = (function(func) {
    return function(...id) {
        return func.apply(document, id);
    }
})(document.getElementById)

var getId = document.getElementById;
var div = getId('div1');
console.log(div.id);

// 函数bind的方法实现
Function.prototype.bind = function(context) {
    var self = this;
    return function(...argus) {
        return self.apply(context, argus);
    }
}
// 函数的call方法的实现
Function.prototype._call = function() {
    var self = this,
        context = [].shift.apply(arguments), // 需要改变的this的指向
        argus = [].slice.call(arguments);
    return self.apply(context, argus);
}

// 封装复杂的bind传值 可以传递两次
Function.prototype._bind = function() {
    var self = this,
        context = [].shift.call(arguments),
        argus = [].slice.call(arguments);
    return function() {
        return self.apply(context, [].concat.call(argus, [].slice.call(arguments)));
    }
}
// 改变this的指向
var func1 = function(...argus) {
    console.log(argus)
}

var func2 = function(...argus) {
    console.log(argus)
};

var func3 = function(...argus) {
    console.log(argus);
}

func1._call(null, 4, 5, 6);
func2.bind(null)(1,2,3);
func3._bind(null, 1,2,3)(4,5,6);


 // 借用其他对象的属性方法
 // 1. 使用new 实现继承
 var A = function(name) {
     this.name = name;
 };

 A.prototype.age = 18;
 var B = function() {
     console.log(this);
    
    // 注意这里的this问题 
    // 函数this的指向有三种
    // 1.函数作为对象的属性被调用时，this指向该对象
    // 2.作为普通函数被调用时，this指向window，严格模式下时undefined
    // 3.作为构造器使用时，如果构造器无返回或者返回一个非Object类型的数据时，this指向构造器内部创建的对象，如果有Object类型的数据返回时，则this与返回的对象无关。
    // 这里A.apply(this)（A只是当作普通的函数进行使用的） 只是在B被当作构造器使用时 指向B的实例。
   
     A.apply(this, arguments);
 }

 B.prototype.getName = function() {
     console.log(this.name);
 }
 var b = new B('bbbb');
 b.getName()

// 借用其他对象的方法
// 类数组当数组操作，借用数组的方法
var obj = {
    length: 0
};
Array.prototype.push.call(obj, 3);
Array.prototype.slice.call(obj);

// 借用数组对象的方法 需要该对象上有 length属性，并且length属性可读写，对象本生可以读写
 */